{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# GNN实例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 实例一"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import json\n",
    "import scipy\n",
    "import numpy as np\n",
    "import networkx as nx\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.optim as optim\n",
    "from torch.autograd import Variable"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数据输入"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# (node, label)集\n",
    "N = [(\"n{}\".format(i), 0) for i in range(1,7)] + \\\n",
    "    [(\"n{}\".format(i), 1) for i in range(7,13)] + \\\n",
    "    [(\"n{}\".format(i), 2) for i in range(13,19)]\n",
    "# 边集\n",
    "E = [(\"n1\",\"n2\"), (\"n1\",\"n3\"), (\"n1\",\"n5\"),\n",
    "     (\"n2\",\"n4\"),\n",
    "     (\"n3\",\"n6\"), (\"n3\",\"n9\"),\n",
    "     (\"n4\",\"n5\"), (\"n4\",\"n6\"), (\"n4\",\"n8\"),\n",
    "     (\"n5\",\"n14\"),\n",
    "     (\"n7\",\"n8\"), (\"n7\",\"n9\"), (\"n7\",\"n11\"),\n",
    "     (\"n8\",\"n10\"), (\"n8\",\"n11\"), (\"n8\", \"n12\"),\n",
    "     (\"n9\",\"n10\"), (\"n9\",\"n14\"),\n",
    "     (\"n10\",\"n12\"),\n",
    "     (\"n11\",\"n18\"),\n",
    "     (\"n13\",\"n15\"), (\"n13\",\"n16\"), (\"n13\",\"n18\"),\n",
    "     (\"n14\",\"n16\"), (\"n14\",\"n18\"),\n",
    "     (\"n15\",\"n16\"), (\"n15\",\"n18\"),\n",
    "     (\"n17\",\"n18\")]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数据Graph显示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 构建Graph\n",
    "G = nx.Graph()\n",
    "G.add_nodes_from(list(map(lambda x: x[0], N)))\n",
    "G.add_edges_from(E)\n",
    "# 设置Graph显示属性，包括颜色，形状，大小\n",
    "ncolor = ['r'] * 6 + ['b'] * 6 + ['g'] * 6\n",
    "nsize = [700] * 6 + [700] * 6 + [700] * 6\n",
    "# 显示Graph\n",
    "plt.figure(1)\n",
    "nx.draw(G, with_labels=True, font_weight='bold', \n",
    "        node_color=ncolor, node_size=nsize)\n",
    "# plt.savefig(\"./images/graph.png\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型构建"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 实现Xi函数，输入一个batch的相邻节点特征向量对ln，返回是s*s的A矩阵\n",
    "# ln是特征向量维度，s为状态向量维度\n",
    "# Input : (N, 2*ln)\n",
    "# Output : (N, S, S)\n",
    "class Xi(nn.Module):\n",
    "    def __init__(self, ln, s):\n",
    "        super(Xi, self).__init__()\n",
    "        self.ln = ln   # 节点特征向量的维度\n",
    "        self.s = s     # 节点的个数\n",
    "        \n",
    "        # 线性网络层\n",
    "        self.linear = nn.Linear(in_features=2 * ln,\n",
    "                                out_features=s ** 2,\n",
    "                                bias=True)\n",
    "        # 激活函数\n",
    "        self.tanh = nn.Tanh()\n",
    "        \n",
    "    def forward(self, X):\n",
    "        bs = X.size()[0]\n",
    "        out = self.linear(X)\n",
    "        out = self.tanh(out)\n",
    "        return out.view(bs, self.s, self.s)\n",
    "\n",
    "# 实现Rou函数\n",
    "# Input : (N, ln)\n",
    "# Output : (N, S)\n",
    "class Rou(nn.Module):\n",
    "    def __init__(self, ln, s):\n",
    "        super(Rou, self).__init__()\n",
    "        self.linear = nn.Linear(in_features=ln,\n",
    "                                out_features=s,\n",
    "                                bias=True)\n",
    "        self.tanh = nn.Tanh()\n",
    "    def forward(self, X):\n",
    "        return self.tanh(self.linear(X))\n",
    "\n",
    "\n",
    "# 实现Hw函数\n",
    "# Input : (N, 2 * ln) \n",
    "#         每一行都是一个节点特征向量和该节点的某一个邻接向量concat\n",
    "#         得到的向量\n",
    "# Input : (N, s)\n",
    "#         对应中心节点的状态向量\n",
    "# Input : (N, )\n",
    "#         对应中心节点的度的向量\n",
    "# Output : (N, s)\n",
    "class Hw(nn.Module):\n",
    "    def __init__(self, ln, s, mu=0.9):\n",
    "        super(Hw, self).__init__()\n",
    "        self.ln = ln\n",
    "        self.s = s\n",
    "        self.mu = mu\n",
    "        \n",
    "        # 初始化网络层\n",
    "        self.Xi = Xi(ln, s)\n",
    "        self.Rou = Rou(ln, s)\n",
    "    \n",
    "    def forward(self, X, H, dg_list):\n",
    "        if type(dg_list) == list:\n",
    "            dg_list = torch.Tensor(dg_list)\n",
    "        elif isinstance(dg_list, torch.Tensor):\n",
    "            pass\n",
    "        else:\n",
    "            raise TypeError(\"==> dg_list should be list or tensor, not {}\".format(type(dg_list)))\n",
    "        A = (self.Xi(X) * self.mu / self.s) / dg_list.view(-1, 1, 1)# (N, S, S)\n",
    "        b = self.Rou(torch.chunk(X, chunks=2, dim=1)[0])# (N, S)\n",
    "        out = torch.squeeze(torch.matmul(A, torch.unsqueeze(H, 2)),-1) + b  # (N, s, s) * (N, s) + (N, s)\n",
    "        return out    # (N, s)\n",
    "\n",
    "class AggrSum(nn.Module):\n",
    "    def __init__(self, node_num):\n",
    "        super(AggrSum, self).__init__()\n",
    "        self.V = node_num\n",
    "    \n",
    "    def forward(self, H, X_node):\n",
    "        # H : (N, s) -> (V, s)\n",
    "        # X_node : (N, )\n",
    "        mask = torch.stack([X_node] * self.V, 0)\n",
    "        mask = mask.float() - torch.unsqueeze(torch.range(0,self.V-1).float(), 1)\n",
    "        mask = (mask == 0).float()\n",
    "        # (V, N) * (N, s) -> (V, s)\n",
    "        return torch.mm(mask, H)\n",
    "\n",
    "# 实现GNN模型\n",
    "class OriLinearGNN(nn.Module):\n",
    "    def __init__(self, node_num, feat_dim, stat_dim, T):\n",
    "        super(OriLinearGNN, self).__init__()\n",
    "        self.embed_dim = feat_dim\n",
    "        self.stat_dim = stat_dim\n",
    "        self.T = T\n",
    "        # 初始化节点的embedding，即节点特征向量 (V, ln)\n",
    "        self.node_features = nn.Embedding(node_num, feat_dim)\n",
    "        # 初始化节点的状态向量 (V, s)\n",
    "        self.node_states = torch.zeros((node_num, stat_dim))\n",
    "        # 输出层\n",
    "        self.linear = nn.Linear(feat_dim+stat_dim, 3)\n",
    "        self.softmax = nn.Softmax()\n",
    "        # 实现Fw\n",
    "        self.Hw = Hw(feat_dim, stat_dim)\n",
    "        # 实现H的分组求和\n",
    "        self.Aggr = AggrSum(node_num)\n",
    "        \n",
    "    # Input : \n",
    "    #    X_Node : (N, )\n",
    "    #    X_Neis : (N, )\n",
    "    #    H      : (N, s)\n",
    "    #    dg_list: (N, )\n",
    "    def forward(self, X_Node, X_Neis, dg_list):\n",
    "        node_embeds = self.node_features(X_Node)  # (N, ln)\n",
    "        neis_embeds = self.node_features(X_Neis)  # (N, ln)\n",
    "        X = torch.cat((node_embeds, neis_embeds), 1)  # (N, 2 * ln)\n",
    "        # 循环T次计算\n",
    "        for t in range(self.T):\n",
    "            # (V, s) -> (N, s)\n",
    "            H = torch.index_select(self.node_states, 0, X_Node)\n",
    "            # (N, s) -> (N, s)\n",
    "            H = self.Hw(X, H, dg_list)\n",
    "            # (N, s) -> (V, s)\n",
    "            self.node_states = self.Aggr(H, X_Node)\n",
    "#             print(H[1])\n",
    "        out = self.linear(torch.cat((self.node_features.weight, self.node_states), 1))\n",
    "        out = self.softmax(out)\n",
    "        return out  # (V, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型训练和评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def CalAccuracy(output, label):\n",
    "    # output : (N, C)\n",
    "    # label : (N)\n",
    "    out = np.argmax(output, axis=1)\n",
    "    res = out - label\n",
    "    return list(res).count(0) / len(res)\n",
    "\n",
    "# 开始训练模型\n",
    "def train(node_list, edge_list, label_list, T, ndict_path=\"./node_dict.json\"):\n",
    "    # 生成node-index字典\n",
    "    if os.path.exists(ndict_path):\n",
    "        with open(ndict_path, \"r\") as fp:\n",
    "            node_dict = json.load(fp)\n",
    "    else:\n",
    "        node_dict = dict([(node, ind) for ind, node in enumerate(node_list)])\n",
    "        node_dict = {\"stoi\" : node_dict,\n",
    "                     \"itos\" : node_list}\n",
    "        with open(ndict_path, \"w\") as fp:\n",
    "            json.dump(node_dict, fp)\n",
    "\n",
    "    # 现在需要生成两个向量\n",
    "    # 第一个向量类似于\n",
    "    #   [0, 0, 0, 1, 1, ..., 18, 18]\n",
    "    # 其中的值表示节点的索引，连续相同索引的个数为该节点的度\n",
    "    # 第二个向量类似于\n",
    "    #   [1, 2, 4, 1, 4, ..., 11, 13]\n",
    "    # 与第一个向量一一对应，表示第一个向量节点的邻居节点\n",
    "\n",
    "    # 首先统计得到节点的度\n",
    "    Degree = dict()\n",
    "    for n1, n2 in edge_list:\n",
    "        # 边的第一个节点的邻接节点为第二个节点\n",
    "        if n1 in Degree:\n",
    "            Degree[n1].add(n2)\n",
    "        else:\n",
    "            Degree[n1] = {n2}\n",
    "        # 边的第二个节点的邻接节点为第一个节点\n",
    "        if n2 in Degree:\n",
    "            Degree[n2].add(n1)\n",
    "        else:\n",
    "            Degree[n2] = {n1}\n",
    "    \n",
    "    # 然后生成两个向量\n",
    "    node_inds = []\n",
    "    node_neis = []\n",
    "    for n in node_list:\n",
    "        node_inds += [node_dict[\"stoi\"][n]] * len(Degree[n])\n",
    "        node_neis += list(map(lambda x: node_dict[\"stoi\"][x],list(Degree[n])))\n",
    "    # 生成度向量\n",
    "    dg_list = list(map(lambda x: len(Degree[node_dict[\"itos\"][x]]), node_inds))\n",
    "    # 准备训练集和测试集\n",
    "    train_node_list = [0,1,2,6,7,8,12,13,14]\n",
    "    train_node_label = [0,0,0,1,1,1,2,2,2]\n",
    "    test_node_list = [3,4,5,9,10,11,15,16,17]\n",
    "    test_node_label = [0,0,0,1,1,1,2,2,2]\n",
    "    \n",
    "    # 开始训练\n",
    "    model = OriLinearGNN(node_num=len(node_list),\n",
    "                         feat_dim=2,\n",
    "                         stat_dim=2,\n",
    "                         T=T)\n",
    "    optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=0.01)\n",
    "    criterion = nn.CrossEntropyLoss(size_average=True)\n",
    "    \n",
    "    min_loss = float('inf')\n",
    "    train_loss_list = []\n",
    "    train_acc_list = []\n",
    "    test_acc_list = []\n",
    "    node_inds_tensor = Variable(torch.Tensor(node_inds).long())\n",
    "    node_neis_tensor = Variable(torch.Tensor(node_neis).long())\n",
    "    train_label = Variable(torch.Tensor(train_node_label).long())\n",
    "    for ep in range(500):\n",
    "        # 运行模型得到结果\n",
    "        res = model(node_inds_tensor, node_neis_tensor, dg_list) # (V, 3)\n",
    "        train_res = torch.index_select(res, 0, torch.Tensor(train_node_list).long())\n",
    "        test_res = torch.index_select(res, 0, torch.Tensor(test_node_list).long())\n",
    "        loss = criterion(input=train_res,\n",
    "                         target=train_label)\n",
    "        loss_val = loss.item()\n",
    "        train_acc = CalAccuracy(train_res.cpu().detach().numpy(), np.array(train_node_label))\n",
    "        test_acc = CalAccuracy(test_res.cpu().detach().numpy(), np.array(test_node_label))\n",
    "        # 更新梯度\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward(retain_graph=True)\n",
    "        optimizer.step()\n",
    "        # 保存loss和acc\n",
    "        train_loss_list.append(loss_val)\n",
    "        test_acc_list.append(test_acc)\n",
    "        train_acc_list.append(train_acc)\n",
    "        \n",
    "        if loss_val < min_loss:\n",
    "            min_loss = loss_val\n",
    "        print(\"==> [Epoch {}] : loss {:.4f}, min_loss {:.4f}, train_acc {:.3f}, test_acc {:.3f}\".format(ep, loss_val, min_loss, train_acc, test_acc))\n",
    "    return train_loss_list, train_acc_list, test_acc_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "train_loss, train_acc, test_acc = train(node_list=list(map(lambda x:x[0], N)),\n",
    "                                        edge_list=E,\n",
    "                                        label_list=list(map(lambda x:x[1], N)),\n",
    "                                        T=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 画出loss和acc曲线"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "epochs = len(train_loss)\n",
    "plt.figure()\n",
    "# 画出train_loss\n",
    "plt.subplot(3,1,1)\n",
    "plt.plot(range(epochs), train_loss)\n",
    "plt.xlim((0,epochs))    # 设置x轴的范围\n",
    "plt.ylabel('loss')      # 设置y周标签\n",
    "plt.yticks(np.arange(0,1.5,0.2))  # 设置y轴刻度\n",
    "\n",
    "# 画出train_acc\n",
    "plt.subplot(3,1,2)\n",
    "plt.plot(range(epochs), train_acc)\n",
    "plt.xlim((0,epochs))\n",
    "plt.ylim((0,1))\n",
    "plt.ylabel('train_acc')\n",
    "plt.yticks(np.arange(0,1,0.1))\n",
    "# 画出test_acc\n",
    "plt.subplot(3,1,3)\n",
    "plt.plot(range(epochs), test_acc)\n",
    "plt.xlim((0,epochs))\n",
    "plt.ylim((0,1))\n",
    "plt.xlabel('epoch')\n",
    "plt.ylabel('test_acc')\n",
    "plt.yticks(np.arange(0,1,0.1))\n",
    "\n",
    "# 保存图片\n",
    "# plt.savefig(\"./images/result.png\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
